package com.zwh.se.constant_variable_operator;

/**
 * 类型转换:在赋值运算或者算数运算的时候，要求数据类型一致，就要进行类型的转换。
 * 类型转换分为两类:自动类型转换、强制类型转换
 *
 * @author yexi
 */
public class  TypeConversion {
    public static void main(String[] args) {

        //自动类型转换:又叫隐式类型转换,指的是在赋值或运算时会对不同数据类型自动进行转换的一种现象(范围小的数据类型直接转换成范围大的数据类型，小->大)
        // int-->double
        double doubleNum = 6;//众所周知,直接定义的整数常数默认是int类型,将其赋值给double时就会自动将其数据自动提升为double
        // 编译错误。这是因为这是打算直接将更大的数据类型转换为更小的数据类型,因为这样做是有可能存在精度丢失的情况的,所以不会进行自动类型转换
        // short shortNum = doubleNum;
        // int-->short、byte、char  那么为什么我们平时这么写却可以做得到？这是因为这种属于特殊情况,是JVM内部的一种优化。
        // 只要所赋予的数是在它们的可表示范围之内,那么赋值的时候就不需要进行强制类型转换直接赋值即可。
        // 特殊情况包括 byte、short、char 这些数据类型
        short shortNum = 6;
        byte byteNum = 6;
        char charNum = 6;
        // 编译错误。打算赋值超过可表示范围的数,从而直接导致编译不通过。
        //shortNum = 1214234;

        // 注意事项:由于char类型的特殊性(存储的最大位数问题)，char类型与byte和short类型不进行自动类型转换
        // shortNum = charNum;
        // byteNum = charNum;
        // charNum = byteNum;
        // charNum = shortNum;

        // 但是由于int类型可表示范围足够大的缘故，所以char可以被赋值给int
        int intNum = charNum;
        // 并且byte类型是可以自动类型转换成short的。由于部分记载的定义上存在一定误差，故而特地说明
        // 所谓的有误差的定义——转换规则：byte、short、char—>int—>long—>float—>double。箭头没错，可是没有对前面部分进行说明，可能会误解。
        // 实际上这个类型级别指的是表达式的运算时，这点在下面可以看到
        shortNum = byteNum;

        //强制类型转换(简称"强转"):又叫显式类型转换(范围大的数据类型强制转换成范围小的数据类型，大->小)
        // 之前说过将大转小是存在着精度丢失的风险的,所以不会进行自动类型转换,但是如果非要转也是可以的,这时就需要强制类型转换
        // 在要强转的数前面加上一个数据类型,即可执行强制类型转换
        int num2 = (int) 6.5;//double--->int
        System.out.println(num2);//精度丢失,故而只剩下了整数部分。这就像是瓶子容量一样，倒进去的水过大会出现溢出。
        num2 = (int) 6.5f;
        System.out.println(num2);
        //精度丢失的底层研究(鉴于浮点数的特殊性姑且使用long转int来弄出效果:
        System.out.println(Long.toBinaryString(Long.MAX_VALUE));
        num2 = (int) Long.MAX_VALUE;
        System.out.println(num2);//-1
        System.out.println(Integer.toBinaryString(num2));
        //可以看出，本来是long的最大值的正数，在强转后变成了一个-1，这是因为所谓强转，就是将有效数字截取到要转换的类型的长度再返回
        //64位的long截取了后面32位的1给了int，而在int的角度，这个值就是-1，这就是精度丢失的本质

        // 自动类型转换在运算上的体现：
        byte b1 = 1;
        short s1 = 2;
        // 编译错误。即便取值都在范围内且使用了byte和short更高类型的short都不能运算，这是因为只要涉及到数字运算，那么都会默认转换到int类型
        //short s2 = b1 + s1;
        int i1 = b1 + s1;
        int i2 = 'a' + 1;
        // 那么如果运算值超过int最大了呢
        int i3 = Integer.MAX_VALUE + 1;//虽然会出现数值溢出，但仍旧是支持运算的，当然这时候就涉及到底层的二进制运算问题导致结果不正确了
        System.out.println(i3);
        // 那么只要使用更高的long类型就好了吗
        long l1 = Integer.MAX_VALUE + 1;//不行，依旧是转换到int类型来运算了
        System.out.println(l1);
        //i3 = Long.MAX_VALUE + 1; 当然long类型是不会被自动转换成int的，因为范围long>int

        //当然这不代表就必须要强转才能计算，这就涉及一个新的概念：
        // 表达式的自动类型提升：
        // 在运算过程中，一个表达式中有多种数据类型的时候，会找出当前表达式中级别最高的那个类型，自动将小的类型转换为大的类型，
        //     然后其余的类型都转换为当前表达式中级别最高的类型进行计算。而int类型是这个规则的最小转换单位。
        // 原因：在进行算术运算操作的时候，必须要求数据类型一致，否则无法操作（涉及的底层硬件暂且不明）
        long l2 = Integer.MAX_VALUE + 1L;
        // 比如运算中带有浮点数，那么浮点数的优先级就更高
        float f1 = 10L + 10.1f;
        double d2 = 10L + 10.1;

        //多种数据类型参与运算的时候，整数类型，浮点类型，字符类型都可以参与运算，唯独布尔类型不可以参与运算。
        //double d3 = 65+1294L+8.4F+6.81+'a'+true;
        double d3 = 65 + 1294L + 8.4F + 6.81 + 'a';
        System.out.println(d3);

        //TIPS:强转为对象时可以强转null值(某些场景下的校验有些用)
        String s = (String) null;

    }
}